#include "weapon.hpp"

static const Vec2f SIZES[Weapon::TYPECT] = 
{
Vec2f(6, 1),	Vec2f(6, 1),	Vec2f(8, 1),
Vec2f(4, 1), 	Vec2f(2, 2),	Vec2f(4, 3),	Vec2f(1, 1),	Vec2f(2, 2),
Vec2f(40, 30)
};
static const int LIFETIMES[Weapon::TYPECT] = 
{
1,		1,		1,
24,		5,		80,		1,		4,
60
};
static const int DAMAGES[Weapon::TYPECT] =
{
2,		3,		3,
1,		1,		1,		1,		1,
256
};
static const int COSTS[Weapon::TYPECT] =
{
0,		0,		0,
1,		1,		2,		4,		5,
0
};

#include "image.hpp"
#include "sound.hpp"

static Image *imgs[Weapon::TYPECT];
static Sound *snds[Weapon::TYPECT];
static Sound *bonussnd;
static Sound *breaksnd;

static const float FPU = .125f;

#include "enemy.hpp"

class Weapon::Type
{
public:
	virtual void Init(Weapon & w)
	{
	}

	virtual void In(Weapon & w)
	{
	}

	virtual void Update(Weapon & w)
	{
	}

	virtual void Hit(Weapon & w, Enemy & en)
	{
	}
};

#include "player.hpp"

class Weapon::Melee : public Weapon::Type
{
	void Update(Weapon & w)
	{
		if (!Plr.Attacking())
		{
			w.Bury();
			return;
		}

		w.pos = Plr.WeaponPos();
		w.vel = Plr.vel;
		w.stghit = Plr.stghit;
		w.Face(Plr.dir);
		w.frame = Plr.frame;

		const Box2f FRAMEBODS[3] =
		{
			Box2f(Vec2f(0, -3), Vec2f(-2, 3)),
			Box2f(Vec2f(0, -3), Vec2f(-1, 5)),
			Box2f(Vec2f(0, -SIZES[MELEE].y*.5f), SIZES[MELEE])
		};

		w.body = FRAMEBODS[(int)w.frame % 3];
		w.body.size.x *= w.dir;
		
		w.StgBreak();
	}
}
	melee;

class Weapon::Bullet : public Weapon::Type
{
	void Update(Weapon & w)
	{
		w.vel.x = w.body.size.x * .5f;

		w.pos += w.vel;
	}
}
	bullet;

class Weapon::Bubble : public Weapon::Type
{
	void Update(Weapon & w)
	{
		w.vel.x = w.dir * .125f;
		w.vel.y -= 1.f/128;

		w.pos += w.vel;
	}

	void Hit(Weapon & w, Enemy & en)
	{
		++w.frame;

		Vec2f dsize (-.25f * w.dir, -.25f);
		Vec2f dpos (dsize * -.5f);
		w.body.size += dsize;
		w.pos += dpos;
	}
}
	bubble;

class Weapon::Cloud : public Weapon::Type
{
	void Update(Weapon & w)
	{
		if (w.body.size.y == 0)
		{
			w.Bury();
			return;
		}

		w.vel.x = w.dir * 1.f/16;
		w.body.size -= Vec2f(w.dir * 1.f/32, 1.f/32);

		w.pos += w.vel;
	}
}
	cloud;

#include "stage.hpp"

class Weapon::Ball : public Weapon::Type
{
	void Update(Weapon & w)
	{
		w.vel.x = w.dir * .125f;
		w.vel.y += 1.f/32;

		for (int ax = Y; ax > NOAXIS; --ax)
		{
			w.pos[ax] += w.vel[ax];

			w.stghit[ax] = Stg.AxisHit(w.body, w.pos, ax);
			if (w.StgHitting(ax))
				w.pos[ax] -= w.stghit[ax];
		}
		
		const float MAXSIZESQ = 32;

		if (w.StgHitting(X) && w.stghit.x != 0)
		{
			if (w.body.size.LenSq() < MAXSIZESQ)
			{
				w.dir = -w.dir;
				w.body.off.x += w.body.size.x;
				w.body.size.x = -w.body.size.x;
			}
			else
				w.Bury();
		}
		else if (w.StgHitting(Y))
		{
			w.vel.y = 0;
			
			if (w.body.size.LenSq() < MAXSIZESQ)
			{
				const float GROWTH = 1.f/64;
				Vec2f newsize (w.body.size);
				newsize += Vec2f(GROWTH * w.dir, GROWTH);

				if ((int)w.body.size.x < (int)newsize.x)
					++w.dmg;

				w.body.size = newsize;
				w.body.off.y -= GROWTH * .5f;
			}
		}
	}
}
	ball;

class Weapon::Option : public Weapon::Type
{
	void Init(Weapon & w)
	{
		w.vel.y = .25f;
	}

	void Update(Weapon & w)
	{
		const float ACCEL = 1.f/32;
		Vec2f fromplr = (w.pos + w.body.Center()) - (Plr.WeaponPos());
		fromplr *= ACCEL;
		w.vel -= fromplr;
		w.pos += Plr.vel;
		w.pos += w.vel;
	}

	void Hit(Weapon & w, Enemy & e)
	{
		w.hp -= e.Damage();
	}
}
	option;

#include "camera.hpp"

class Weapon::Nuke : public Weapon::Type
{
public:
	void Init(Weapon & w)
	{
		w.grid.Resize(w.body.size.x + 2, w.body.size.y + 2);
	}

	void Update(Weapon & w)
	{
		const Vec2f OFF (-.0625f, .125f);
		w.pos = Cam.off - (OFF * 4);
		w.pos += OFF * (w.hp % 8);
		w.alpha = std::min(1.f, .125f * w.hp);
		--w.hp;
	}
}
	nuke;

static Weapon::Type *TYPES[Weapon::TYPECT] = 
{
&melee, 	&melee,		&melee,	
&bullet,	&bubble,	&cloud,		&ball,  	&option,
&nuke
};

int Weapon::TDamage(int t)
{
	return DAMAGES[t];
}

int Weapon::TCost(int t)
{
	return COSTS[t];
}

Weapon::Weapon()
{
	type = 0;
	typen = NOTYPE;
	frame = 0;
	dir = 1;
}

Weapon::~Weapon()
{
}


bool Weapon::Open()
{
	const char *STRINGS[TYPECT] =
	{
		"melee", "melee2", "melee3",
		"bullet", "bubble", "cloud", "ball", "option",
		"nuke"
	};
	const char *SNDSTRINGS[TYPECT] =
	{
		"melee", "melee", "melee",
		"bullet", "bubble", "cloud", "ball", "option",
		"nuke"
	};

	const char *DIR = "weapon/";

	int got = AllImgs.BatchGet(imgs, TYPECT, DIR, STRINGS);
	got += AllSnds.BatchGet(snds, TYPECT, DIR, SNDSTRINGS);
	bonussnd = AllSnds.Get("item/point");
	breaksnd = AllSnds.Get("break");
	got += (bonussnd != 0);
	return true;//got == 2 * TYPECT;
}

#include "cue.hpp"

void Weapon::Init(float x, float centy, Cue & cue)
{
	Reset();
	typen = cue.Num();
	type = TYPES[typen];
	hp = LIFETIMES[typen];
	dmg = DAMAGES[typen];
	kills = 0;
	body.size = SIZES[typen];
	body.size.x *= cue.Dir();
	body.off = Vec2f(0, body.size.y * -.5f);
	pos = Vec2f(x, centy);
	Face(cue.Dir());
	img = imgs[typen];
	snd = snds[typen];
	if (type)
		type->Init(*this);
}

#include "input.hpp"

void Weapon::In()
{
	if (type)
		type->In(*this);
}

#include "video.hpp"

void Weapon::Update()
{
	if (type)
	{
		type->Update(*this);
	}

	if (!Live() || Gone())
		Bury();
}

#include "game.hpp"

#include "effect.hpp"

void Weapon::Hit(Enemy & en)
{
	if (type && en.Vulnerable())
	{
		type->Hit(*this, en);
		if (IsShot() && en.hp - Damage() <= 0 && en.Damage())
		{
			++kills;
			if (kills > 1)
			{
				Game.Score(1000 * (1 << (kills - 2)));
				snd = bonussnd;
				Cue bonuscue(Effect::ITM_POINT_XL + kills - 2);
				if (kills < 5)
				{
					Efx.Init(en.pos.x + en.body.AxCenter(X),
						en.pos.y + en.body.AxCenter(Y),
						bonuscue);
				}
			}
		}
	}
}

#include "item.hpp"
#include "cue.hpp"
#include "stage.hpp"

void Weapon::StgBreak()
{
	const int BRKSIZE = 2, BRKHSIZE = BRKSIZE/2;

	Vec2i lefttop (pos.x + body.AxMin(X) - BRKHSIZE,
			pos.y + body.AxMin(Y) - BRKHSIZE);
	Vec2i rightbot (pos + body.RightBot());

	for (int r = lefttop.y; r <= rightbot.y; ++r)
	{
		for (int c = lefttop.x; c <= rightbot.x; ++c)
		{
			Cue cue (Cues.Get(c, r));

			if (cue.Num() != cue.BREAK)
				continue;

			for (int br = r; br < r + BRKSIZE; ++br)
			{
				for (int bc = c; bc < c + BRKSIZE; ++bc)
				{
					Stg.Put(bc, br, 0);
					Cue icue (Cues.Get(bc, br));

					if (!icue.IsItem())
						continue;
					Itms.Init(c+BRKHSIZE, r+BRKHSIZE, icue);
				}
			}

			snd = breaksnd;
			Cues.Put(c, r, cue.NONE);
		}
	}
}

ActPool<Weapon, 8> Wpns;
